#include "main.h"

#define MAX_ANGLE_INSIDE 5

/* 
 * ===  FUNCTION  ======================================================================
 *         Name:  check_expr
 *  Description:  
 *  Return: 0 success, 1 failed
 * =====================================================================================
 */
gint check_expr (gchar *ip_file)
{
  GError *error = NULL;
  GIOChannel *ip = g_io_channel_new_file (ip_file, "r", &error);

  if (!ip) {
    g_print ("Can't open file %s to read: %s\n", ip_file, error->message);
    g_error_free (error);
    return 2;
  }

  // begin check
  gsize ter_pos;
  gint find_error = 0;
  GString *expr = g_string_new (NULL);
  while (1) {
    GIOStatus status = g_io_channel_read_line_string (ip, expr, &ter_pos, &error);
    if (status == G_IO_STATUS_EOF) {
      // end
      break;
    } else if (status == G_IO_STATUS_NORMAL) {
      // check expression one by one
      g_string_truncate (expr, ter_pos);
      if(check_expr_each (expr)) {
        find_error = 1;
        break;
      }
    } else {
      g_print ("Can't read line from file %s\n: %s", ip_file, error->message);
      g_error_free (error);
      find_error = 1;
      break;
    }
  }

  g_string_free (expr, TRUE);
  g_io_channel_shutdown (ip, TRUE, NULL);

  if (find_error)
    return 1;

  return 0;
}


/* 
 * ===  FUNCTION  ======================================================================
 *         Name:  check_expr_each
 *  Description:  
 * =====================================================================================
 */
gint check_expr_each (GString *expr)
{
  g_debug ("[check_expr_each]: len = %d, (%s)", expr->len, expr->str);

  if (check_token_match (expr))
    return 1;

  if (check_angle_stand_along (expr))
    return 1;

  if (check_angle_overflow (expr))
    return 1;

  return 0;
}


/* 
 * ===  FUNCTION  ======================================================================
 *         Name:  check_token_match
 *  Description:  
 * =====================================================================================
 */
gint check_token_match (GString *expr)
{
  gint i;
  GQueue *stack = g_queue_new ();

  for (i = 0; i < expr->len; i++) {
    if (expr->str[i] == TOKEN_BRACE_START) {
      g_queue_push_tail (stack, "TOKEN_BRACE_START");
    }

    if (expr->str[i] == TOKEN_CURLY_START) {
      g_queue_push_tail (stack, "TOKEN_CURLY_START");
    }

    if (expr->str[i] == TOKEN_ANGLE_START) {
      g_queue_push_tail (stack, "TOKEN_ANGLE_START");
    }

    if (expr->str[i] == TOKEN_BRACE_END) {
      if ((gchar *)g_queue_peek_tail(stack) == "TOKEN_BRACE_START") {
        g_queue_pop_tail (stack);
        continue;
      }

      g_queue_push_tail (stack, "TOKEN_BRACE_END");
    }

    if (expr->str[i] == TOKEN_CURLY_END) {
      if ((gchar *)g_queue_peek_tail(stack) == "TOKEN_CURLY_START") {
        g_queue_pop_tail (stack);
        continue;
      }

      g_queue_push_tail (stack, "TOKEN_CURLY_END");
    }

    if (expr->str[i] == TOKEN_ANGLE_END) {
      if ((gchar *)g_queue_peek_tail(stack) == "TOKEN_ANGLE_START") {
        g_queue_pop_tail (stack);
        continue;
      }

      g_queue_push_tail (stack, "TOKEN_ANGLE_END");
    }

  }

  if (!g_queue_is_empty (stack)) {
    g_critical ("[check_token_match]: token is not match, please check");
    return 1;
  }

  return 0;
}



/* 
 * ===  FUNCTION  ======================================================================
 *         Name:  check_mutex_inside
 *  Description:  
 * =====================================================================================
 */
gint check_mutex_inside (GString *expr)
{

}


/* 
 * ===  FUNCTION  ======================================================================
 *         Name:  check_angle_stand_along
 *  Description:  
 * =====================================================================================
 */
gint check_angle_stand_along (GString *expr)
{
  gint i, in = 0, error;

  GQueue *stack = g_queue_new ();
  
  for (i = 0; i < expr->len; i++) {
    if (expr->str[i] == TOKEN_CURLY_START || \
        expr->str[i] == TOKEN_BRACE_START) {
      g_queue_push_tail (stack, "TOKEN_ANGLE_START");
    } else if (expr->str[i] == TOKEN_CURLY_END || \
               expr->str[i] == TOKEN_BRACE_END) {
      g_queue_pop_tail (stack);
    }

    if (expr->str[i] == TOKEN_ANGLE_START) {
      // check outside, only check left, because token match checked before
      if (!g_queue_is_empty (stack)) {
        g_critical ("[check_angle_stand_along]: token angle should not inside any other token");
        return 1;
      }
      in = 1;
    } else if (expr->str[i] == TOKEN_ANGLE_END) {
      in = 0;
    } else {
      // check inside
      if (in){
        if (is_token_start (expr->str[i]) || \
            is_token_end (expr->str[i])) {
          g_critical ("[check_angle_stand_along]: token angle should not contain any other token");
          return 1;
        }
      }
    }

  }
  
  return 0;
}


/* 
 * ===  FUNCTION  ======================================================================
 *         Name:  check_angle_overflow
 *  Description:  
 * =====================================================================================
 */
gint check_angle_overflow (GString *expr)
{
  gint i, in = 0, count = 0;
  for (i = 0; i < expr->len; i++) {
    if (expr->str[i] == TOKEN_ANGLE_START) {
      in = 1;
    } else if (expr->str[i] == TOKEN_ANGLE_END) {
      in = 0;
    } else {
      if (in) {
        if (expr->str[i] != '-' && \
            expr->str[i] != ' ') {
          count++;
        }
      }
    }
  }

  if (count > MAX_ANGLE_INSIDE) {
    g_critical ("[check_angle_overflow]: max char inside angle should < %d", MAX_ANGLE_INSIDE);
    return 1;
  }

  return 0;
}
